package me.minecraft.plugin.itemsave.manager;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import lombok.Getter;
import me.minecraft.plugin.itemsave.ItemSave;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.Optional;

/**
 * @author langle__
 * @version 1.0
 */
@Getter
public class FileManager {

    public static final FileManager INSTANCE = new FileManager();

    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

    private final File itemFolder;

    private FileManager() {
        itemFolder = new File(ItemSave.instance.getDataFolder(), "items");
        if (!itemFolder.exists() && !itemFolder.mkdirs()) {
            ItemSave.instance.getLogger().severe("Failed to create the items folder.");
        }
    }

    public boolean exists(@NotNull String name) {
        return this.getItemFile(name).exists();
    }

    @NotNull
    public String ensureJsonExtension(@NotNull String name) {
        return name.endsWith(".json") ? name : name + ".json";
    }

    @NotNull
    public File getItemFile(@NotNull String name) {
        return new File(itemFolder, ensureJsonExtension(name));
    }

    public boolean saveItem(@NotNull String fileName, @NotNull ItemStack item) {
        File file = getItemFile(fileName);
        return saveItem(file, item);
    }

    public void asyncSave(@NotNull File file, @NotNull ItemStack item) {
        Bukkit.getScheduler().runTaskAsynchronously(ItemSave.instance, () -> this.saveItem(file, item));
    }

    public boolean saveItem(@NotNull File file, @NotNull ItemStack item) {
        net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
        Codec<net.minecraft.world.item.ItemStack> codec = net.minecraft.world.item.ItemStack.CODEC;
        Optional<JsonElement> optionalJson = codec.encodeStart(JsonOps.INSTANCE, nmsItem).result();

        if (optionalJson.isEmpty()) return false;

        JsonElement json = optionalJson.get();
        String jsonString = GSON.toJson(json);

        try (BufferedWriter writer = Files.newBufferedWriter(
                file.toPath(),
                StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.WRITE,
                StandardOpenOption.TRUNCATE_EXISTING
        )) {
            writer.write(jsonString);
        } catch (IOException e) {
            ItemSave.instance.getLogger().warning("Failed to save item: " + file.getAbsolutePath());
            return false;
        }

        return true;
    }

    @Nullable
    public ItemStack loadItem(@NotNull String fileName) {
        File file = getItemFile(fileName);
        return loadItem(file);
    }

    @Nullable
    public ItemStack loadItem(@NotNull File file) {
        if (!file.exists()) return null;

        JsonObject json;
        try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
            json = GSON.fromJson(reader, JsonObject.class);
        } catch (IOException e) {
            ItemSave.instance.getLogger().warning("Failed to load item: " + file.getAbsolutePath());
            return null;
        }

        Codec<net.minecraft.world.item.ItemStack> codec = net.minecraft.world.item.ItemStack.CODEC;
        Optional<net.minecraft.world.item.ItemStack> nmsItem = codec.parse(JsonOps.INSTANCE, json).result();

        return nmsItem.map(CraftItemStack::asBukkitCopy).orElse(null);
    }

    public boolean deleteItem(@NotNull String fileName) {
        File file = getItemFile(fileName);
        return deleteItem(file);
    }

    public boolean deleteItem(@NotNull File file) {
        if (!file.exists()) return true;
        return file.delete();
    }

}
